/*
Kakine
ActionScript Framework
Copyright © 2011, PUNY, LLC.

This file is part of KAKINE.

KAKINE is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

KAKINE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with KAKINE.  If not, see <http://www.gnu.org/licenses/>.
*/ 
package com.puny.utils
{
	import com.puny.mvc.helpers.FrameHelper;
	import com.puny.mvc.helpers.IHelper;
	
	import flash.display.FrameLabel;
	import flash.display.MovieClip;
	import flash.events.Event;
	
	/**
	 * This is a class to handle animation in a <code>MovieClip</code>
	 * A labeled time line should be considered a state of an animation.
	 * If a label name starts with an underscoore(_), the state will be played in loop.
	 * @author puny
	 * 
	 */
	public class MovieClipStates extends Object
	{
		public static const LOOP_PREFIX:String = "_";
		private var _mc:MovieClip;
		private var _labels:Array;
		private var _loopPrefix:Object = new Object();
		private var _frameEnd:Object = new Object();
		private var _label:String;
		private var _helper:IHelper;
		private var _callBack:Function;
		private var _cancel:Boolean = true;
		/**
		 * 
		 * @param mc an instance of a <code>MovieClip</code>
		 * 
		 */
		public function MovieClipStates(mc:MovieClip)
		{
			_mc = mc;
			_mc.addEventListener(Event.REMOVED_FROM_STAGE, cleanUpHandler);
			super();
		}
		
		/**
		 * Output a sample code to handle animation in console. 
		 * @param pkg package name
		 * @param cls class name
		 * @param mc MovieClip instance
		 * 
		 * @example
		 * The sample code below outputs a sample class in a console.
		 * Copy and paste the code in an appropriate file.
		 * <listing version="3.0">
		 * 	MovieClipStates.codeGenerator("com.puny.test", "MyAnimatedAsset", mc);
		 * </listing>
		 */
		public static function codeGenerator(pkg:String, cls:String, mc:MovieClip):void {
			var label:String;
			var code:String;
			var frameLabel:FrameLabel;
			trace("package" , pkg, "{");
			trace("\timport com.puny.utils.MovieClipStates;");
			trace("\timport flash.display.MovieClip;");
			trace("\tpublic class", cls, "extends MovieClipStates {");
			for each(frameLabel in mc.currentLabels) {
				label = frameLabel.name.replace(/^_/, "");
				code = label.replace(/ /g, "_").toUpperCase();
				trace("\t\tpublic static const", code+":String = \""+label+"\";");
			}
			trace("\t\tpublic function", cls+"(mc:MovieClip) {");
			trace("\t\t\tsuper(mc);");
			trace("\t\t\tcheckLabels([");
			var count:int = 0;
			var comma:String = "";
			for each(frameLabel in mc.currentLabels) {
				label = frameLabel.name.replace(/^_/, "");
				code = label.replace(/ /g, "_").toUpperCase();
				count++;
				if (count == mc.currentLabels.length) {
					comma = "";
				} else {
					comma = ",";
				}
				trace("\t\t\t\t\t", code+comma);
			}
			trace("\t\t\t]);");
			trace("\t\t}");
			trace("\t}");
			trace("}");
		}
		
		/**
		 * Check if the given <code>MovieClip</code> in a constructor has the given label names.
		 * In the list of labels, underscore(_) as a prefix is not required. 
		 * @param labels
		 * 
		 */
		public function checkLabels(labels:Array):void {
			var label:String;
			var frameLabel:FrameLabel;
			var check:Boolean;
			var loopLabel:String;

			for each(label in labels) {
				check = false;
				loopLabel = LOOP_PREFIX + label;
				for each(frameLabel in _mc.currentLabels) {
					check = check || frameLabel.name == label;
					check = check || frameLabel.name == loopLabel;
				}
				if (!check) {
					throw new Error("ERROR:"+label+" is Missing in "+this);
				}
			}
			_labels = labels;
			analyzeLabels();
		}
		
		private function analyzeLabels():void {
			var i:int;
			for (i = 0;i < _mc.totalFrames;i++) {
				_mc.gotoAndStop(i+1);
				_frameEnd[_mc.currentLabel] = _mc.currentFrame;
			}
			for each(var frame:FrameLabel in _mc.currentLabels) {
				for each(var label:String in _labels) {
					_loopPrefix[label] = (frame.name == LOOP_PREFIX + label) ? LOOP_PREFIX : (_loopPrefix[label] != null ? _loopPrefix[label] : "");
//					trace(label,_loopPrefix[label]);
				}
//				trace(frame.name,":",frame.frame,"-",_frameEnd[frame.name]);;
			}
			_mc.gotoAndStop(1);
			
		}
		
		/**
		 * 
		 * @param value
		 * @param callBack
		 * 
		 */
		public function enhancedPlay(value:String,callBack:Function=null,cancel:Boolean = true):void {
			enhancedStop();
			_cancel = cancel;
			_callBack = callBack;
			_label = value;
			_helper = FrameHelper.setup(_mc, before, loop, after);
		}
		
		public function enhancedStop():void {
			if (_helper != null) {
				if (_cancel) {
					_cancel = true;
					_helper.cancel();
					if (_mc != null) {
//						trace("stop",_mc.x, _mc);
						_mc.stop();
					}
				} else {
//					trace("skipped",_mc.x, _mc);
					_cancel = true;
					_helper.skip();
				}
			}
		}
		
		private function before():void {
			_mc.gotoAndPlay(label(_label));
		}
		
		private function loop():Boolean {
			if (_mc == null) {
				return false;
			}
			if (_mc.currentFrame >= endFrame(_label)) {
				if (label(_label).charAt(0) == "_") {
					_mc.gotoAndPlay(label(_label));
				} else {
					_mc.gotoAndStop(endFrame(_label));
					return false;
				}
			}
			return true;
		}
		
		private function after():void {
			if (_mc != null) {
				_mc.stop();
			}
			_helper = null;
			if (_callBack != null) {
				_callBack();
			}
		}
		
		public function label(value:String):String {
			return (_loopPrefix[value] == undefined ? "" : _loopPrefix[value])+value;			
		}
		
		
		public function firstFrame(value:String):int {
			for each(var frame:FrameLabel in _mc.currentLabels) {
				if (frame.name == label(value)) {
					return frame.frame;
				}
			}
			return -1;
		}
		
		public function endFrame(value:String):int {
			return _frameEnd[label(value)];
		}
		
		private function cleanUp():void {
			enhancedStop();
			_mc = null;
			_callBack = null;
		}
		
		public function pause():void {
			if(_helper != null) {
				_helper.pause();
				_mc.stop();
			}
		}
		
		public function resume():void {
			if(_helper != null) {
				_helper.resume();
				_mc.play();
			}
		}
		
		private function cleanUpHandler(event:Event):void {
			_mc.removeEventListener(Event.REMOVED_FROM_STAGE, cleanUpHandler);
			cleanUp();
		}
	}
}